package question

import "sync"

// 交替打印数字和字母
// 使用两个 goroutine，一个打印数字，另一个打印字母，最终输出结果为 12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728
func PrintNumLetterAlternateOne() {

	// 基本思路：
	// 1. 创建两个 goroutine，一个用于打印数字，另一个用于打印字母。
	// 2. 使用两个通道（channel）来协调两个 goroutine 的执行顺序。
	// 3. 数字 goroutine 打印两个数字后，向字母 goroutine 发送信号，字母 goroutine 打印两个字母后，向数字 goroutine 发送信号。
	// 4. 重复上述过程直到字母打印完毕，字母 goroutine 结束时，通知数字 goroutine 最后执行一次然后结束。

	// 创建两个通道用于数字和字母的传递
	// 只要通道中得到了 true 的信号，就表示可以进行打印操作；得到了 false 的信号，就表示打印操作结束，退出goroutine
	numCh := make(chan bool)
	letterCh := make(chan bool)

	// goroutine的同步操作
	// 使用 WaitGroup 来确保主函数等待所有 goroutine 完成后再退出
	var wg sync.WaitGroup
	wg.Add(2) // 两个 goroutine

	// 启动数字 goroutine
	// 数字goroutine每次输出两个自增的数字（从1开头），然后通过numCh通道通知字母goroutine开始输出
	go func() {
		defer wg.Done()
		i := 1
		for {
			// 等待字母 goroutine 的信号
			_, ok := <-numCh
			if !ok {
				// 通道关闭，退出 goroutine
				// close(letterCh) // 关闭字母通道，通知字母 goroutine 结束
				return
			}
			// 打印两个数字
			for j := 0; j < 2; j++ {
				print(i)
				i++
			}
			// 通知字母 goroutine 开始打印
			letterCh <- true
		}

	}()

	// 启动字母 goroutine
	go func() {
		defer wg.Done()
		i := 'A'
		for {
			// 等待数字 goroutine 的信号
			_, ok := <-letterCh
			if !ok {
				// 通道关闭，退出 goroutine
				// close(numCh) // 关闭数字通道，通知数字 goroutine 结束
				return
			}
			// 打印两个字母
			for j := 0; j < 2; j++ {
				if i > 'Z' {
					// 超过 'Z' 则结束打印
					close(numCh) // 关闭数字通道，通知数字 goroutine 结束
					return
				}
				print(string(i))
				i++
			}
			// 通知数字 goroutine 开始打印
			numCh <- true
		}
	}()

	// 启动打印流程，首先通知数字 goroutine 开始打印
	numCh <- true

	// 同步阻塞等待所有 goroutine 完成
	wg.Wait()
}

// 优化一下如上方案：
func PrintNumLetterAlternateTwo() {

	// 更健壮的实现：使用一个单独的 done channel 来通知退出，避免在多个 goroutine 间关闭同一个信号通道导致的 panic。
	// 思路：
	// - numCh / letterCh 只用于传递“可以打印”的信号，不负责关闭。
	// - done 用于通知所有 goroutine 结束，由字母 goroutine 在完成后负责关闭 done（单一关闭责任）。
	// - 各 goroutine 在每次等待信号时使用 select 同时监听 done，从而能在 done 关闭时安全退出。

	numCh := make(chan bool)
	letterCh := make(chan bool)
	done := make(chan struct{})

	var wg sync.WaitGroup
	wg.Add(2)

	// 数字 goroutine
	go func() {
		defer wg.Done()
		i := 1
		for {
			select {
			case <-done:
				return
			case _, ok := <-numCh:
				if !ok {
					// 如果 numCh 被意外关闭，也安全退出
					return
				}
			}

			// 打印两个数字
			for j := 0; j < 2; j++ {
				// 在打印前也检查是否收到 done，以便快速退出
				select {
				case <-done:
					return
				default:
				}
				print(i)
				i++
			}

			// 通知字母 goroutine 开始打印（非阻塞发送以避免在某些边界情形下死锁）
			select {
			case <-done:
				return
			case letterCh <- true:
			}
		}
	}()

	// 字母 goroutine
	go func() {
		defer wg.Done()
		i := 'A'
		for {
			select {
			case <-done:
				return
			case _, ok := <-letterCh:
				if !ok {
					return
				}
			}

			// 打印两个字母
			for j := 0; j < 2; j++ {
				if i > 'Z' {
					// 打印完成，关闭 done（单一关闭方），让其他 goroutine 安全退出
					close(done)
					return
				}
				select {
				case <-done:
					return
				default:
				}
				print(string(i))
				i++
			}

			// 通知数字 goroutine 开始打印
			select {
			case <-done:
				return
			case numCh <- true:
			}
		}
	}()

	// 启动打印流程
	numCh <- true

	// 等待所有 goroutine 结束
	wg.Wait()

}
